/*
 *  Copyright 2004 Blandware (http://www.blandware.com)
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.blandware.atleap.webapp.util.core;

import com.blandware.atleap.common.Constants;
import com.blandware.atleap.common.util.QueryInfo;
import com.blandware.atleap.common.util.ConvertUtil;
import com.blandware.atleap.model.core.*;
import com.blandware.atleap.service.core.LayoutManager;
import com.blandware.atleap.service.core.MenuManager;
import com.blandware.atleap.service.core.PageManager;
import com.blandware.atleap.service.core.RoleManager;
import com.blandware.atleap.service.exception.BeanNotFoundException;
import com.blandware.atleap.webapp.exception.MenuUtilException;
import com.blandware.atleap.webapp.menu.MenuComponent;
import com.blandware.atleap.webapp.menu.MenuRepository;
import com.blandware.atleap.webapp.menu.LoadableResourceException;
import com.blandware.atleap.webapp.struts.HeritableComponentDefinition;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.beanutils.Converter;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.validator.GenericValidator;
import org.apache.struts.tiles.TilesUtil;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;

/**
 * <p>Provides some methods to work with menus</p>
 * <p><a href="MenuUtil.java.html"><i>View Source</i></a></p>
 *
 * @author Sergey Zubtsovskiy <a href="mailto:sergey.zubtsovskiy@blandware.com">&lt;sergey.zubtsovskiy@blandware.com&gt;</a>
 * @version $Revision: 1.25 $ $Date: 2007/04/19 08:43:31 $
 */
public final class MenuUtil {

	protected transient final Log log = LogFactory.getLog(MenuUtil.class);

	protected transient HttpServletRequest request;

	protected transient ServletContext servletContext;

	protected transient ApplicationContext applicationContext;

    protected transient String contextPath;

    /**
     * Menu manager to work with
     */
	protected transient MenuManager menuManager;

	/**
	 * Creates new instance of MenuUtil
	 *
	 * @param request HttpServletRequest instance
	 */
	public MenuUtil(HttpServletRequest request) {
		this.request = request;
		this.servletContext = request.getSession().getServletContext();
        this.contextPath = request.getContextPath();
        this.applicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
		this.menuManager = (MenuManager) applicationContext.getBean(Constants.MENU_MANAGER_BEAN);
	}

    /**
     * Creates menu util for initialization purposes
     *
     * @param servletContext Servlet context
     * @param contextPath Context path 
     */
    public MenuUtil(ServletContext servletContext, String contextPath) {
        this.request = null;
        this.servletContext = servletContext;
        this.contextPath = contextPath;
        this.applicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
        this.menuManager = (MenuManager) applicationContext.getBean(Constants.MENU_MANAGER_BEAN);
    }


    public List getMenuItems(Long parentItemId, String layoutDefinition, String pageUri, boolean deleteInvisible) throws MenuUtilException {

		List owners = lookupOwners(layoutDefinition, pageUri);
		List menuItems = lookupItems(owners, parentItemId, deleteInvisible);

		// set location
		try {
			for ( ListIterator iterator = menuItems.listIterator(); iterator.hasNext(); ) {
				MenuItem menuItem = (MenuItem) iterator.next();
				menuItem.setLocation(getLocation(menuItem, WebappConstants.URL_TYPE_CONTEXT_RELATIVE));
			}
		} catch ( MalformedURLException e ) {
			if ( log.isFatalEnabled() ) {
				StringWriter sw = new StringWriter();
				e.printStackTrace(new PrintWriter(sw));
				log.fatal("Setting location failed. See the stack trace for details.");
				log.fatal(sw.toString());
			}
		}
		return menuItems;
	}

	public MenuComponent getMenuComponent(String itemIdentifier, String localeIdentifier, String layoutDefinition, String pageUri, boolean deleteInvisible) throws MenuUtilException {

		LinkedList owners = lookupOwners(layoutDefinition, pageUri);

		int i = 0;
		MenuItem menuItem = null;
		for ( i = 0; i < owners.size(); i++ ) {
			Localizable owner = (Localizable) owners.get(i);
			Long ownerId = null;
			if ( owner != null ) {
				ownerId = owner.getId();
			}
			menuItem = menuManager.findMenuItemByIdentifierAndParentAndOwner(itemIdentifier, null, ownerId);
			if ( menuItem != null ) {
				break;
			}
		}

		if ( menuItem == null ) {
			// nothing found
			return null;
		}

		Byte visibility = menuItem.getVisibility();

		if ( menuItem.isVisibilityHeritable() ) {
			visibility = new Byte(MenuItem.VISIBILITY_VISIBLE);
		}

		// search for redefinition of this item from the another side of list (starting from page)
		// i - number of layer, on which we have found the item

		int j;
		MenuItem redefinitionItem = null;
		for ( j = owners.size() - 1; j > i; j-- ) {
			Localizable owner = (Localizable) owners.get(j);
			Long ownerId = null;
			if ( owner != null ) {
				ownerId = owner.getId();
			}
			redefinitionItem = menuManager.findRedefinitionItem(menuItem.getId(), ownerId);
			if ( redefinitionItem != null ) {
				menuItem.setVisibility(redefinitionItem.getVisibility());
			}
		}

		if ( redefinitionItem == null && menuItem.isVisibilityHeritable() ) {
			menuItem.setVisibility(visibility);
		} else if ( redefinitionItem != null && menuItem.isVisibilityHeritable() ) {
			if ( j > i + 1 ) {
				for ( int k = j - 1; k > i; k-- ) {
					Localizable owner = (Localizable) owners.get(j);
					Long ownerId = null;
					if ( owner != null ) {
						ownerId = owner.getId();
					}
					redefinitionItem = menuManager.findRedefinitionItem(menuItem.getId(), ownerId);
					if ( redefinitionItem != null && !redefinitionItem.isVisibilityHeritable() ) {
						menuItem.setVisibility(redefinitionItem.getVisibility());
						break;
					}

				}
			}
			if ( menuItem.isVisibilityHeritable() ) {
				menuItem.setVisibility(visibility);
			}
		}

		// check visibility
		if ( menuItem.isInvisible() && deleteInvisible ) {
			return null;
		}

		return convertToMenuComponent(owners, menuItem, localeIdentifier, deleteInvisible);
	}

	/**
	 * Processes list of menu components and completes it from elements acquired from persistence layer
	 *
	 * @param owners          List of owners from which to get items
	 * @param parentItemId    ID of parent component
	 * @param deleteInvisible Whether or not to delete components if they are invisible
	 * @throws MenuUtilException wraps any catched exception
	 */
	protected List lookupItems(List owners, Long parentItemId, boolean deleteInvisible) throws MenuUtilException {

		List items = new ArrayList();
		try {

			// iterate over list of owners
			for ( Iterator i = owners.iterator(); i.hasNext(); ) {
				Localizable owner = (Localizable) i.next();
				QueryInfo queryInfo = new QueryInfo();
				Map queryParameters = new HashMap();

				if ( owner != null ) {
					queryParameters.put("ownerId", owner.getId());
				}
				queryParameters.put("parentItemId", parentItemId);

				queryInfo.setQueryParameters(queryParameters);

				List menuItems = menuManager.listMenuItems(queryInfo).asList();

				for ( Iterator j = menuItems.iterator(); j.hasNext(); ) {

					MenuItem menuItem = (MenuItem) j.next();

                    // find menu item for which current one is redifinition
					MenuItem oldItem = null;
					if ( menuItem.isRedefinition() ) {
						oldItem = findMenuItem(items, menuItem.getOrigItem().getId());
					}

                    // modifiedOldItem is that item that is a result of 'addition'
                    // of oldItem (which is redefined) and redifinition
					MenuItem modifiedOldItem = null;
					if ( oldItem != null ) {
						modifiedOldItem = new MenuItem();
						// copy properties
						WebappUtil.copyProperties(modifiedOldItem, oldItem, request);
					}

					if ( menuItem.getPosition() != null && modifiedOldItem != null ) {
                        // position is specified and that's redifinition:
                        // put it to specified position and put item that was
                        // in that position to position of redefined item
						int pos = menuItem.getPosition().intValue();
						if ( pos >= items.size() ) {
							pos = items.size() - 1;
						}
						int oldPos = items.indexOf(oldItem);

						modifiedOldItem.setVisibility(menuItem.getVisibility());

						MenuItem tmp = (MenuItem) items.get(pos);
						items.set(oldPos, tmp);
						items.set(pos, modifiedOldItem);
					} else if ( menuItem.getPosition() != null && modifiedOldItem == null ) {
                        // position is specified and that's not redifinition:
                        // just add to list in specified position
						int pos = menuItem.getPosition().intValue();
						if ( pos < items.size() ) {
							items.add(pos, menuItem);
						} else {
							items.add(menuItem);
						}
					} else if ( menuItem.getPosition() == null && modifiedOldItem == null ) {
                        // position is not specified and that's not redifinition:
                        // just add to the end of list
						items.add(menuItem);
					} else if ( menuItem.getPosition() == null && modifiedOldItem != null ) {
                        // position is not specified and that's redifinition:
                        // position is inherited from old item
						int tmpPos = items.indexOf(oldItem);
						modifiedOldItem.setVisibility(menuItem.getVisibility());
						items.set(tmpPos, modifiedOldItem);
					}
				}
			}

			// delete invisible elements
			for ( Iterator i = items.iterator(); i.hasNext(); ) {
				MenuItem menuItem = (MenuItem) i.next();
				if ( menuItem.isInvisible() && deleteInvisible ) {
					i.remove();
				}
			}

		} catch ( Exception e ) {
			throw new MenuUtilException(e);
		}
		return items;
	}


	/**
	 * Creates hierarchy of menu components
	 *
	 * @param owners          List of owners (layers)
	 * @param menuItem        Menu item to convert
	 * @param deleteInvisible Whether or not to delete invisible elements
	 * @return Menu component with all childs components, with their child components and so on
	 * @throws MenuUtilException if something wrong happens
	 */
	protected MenuComponent convertToMenuComponent(List owners, MenuItem menuItem, String localeIdentifier, boolean deleteInvisible) throws MenuUtilException {
		MenuComponent menuComponent = new MenuComponent();

		// convert item to component
		try {
			WebappUtil.copyProperties(menuComponent, menuItem, request);
			menuComponent.setName(menuItem.getIdentifier());

			// set roles
			StringBuffer roles = null;
			if ( menuItem.getRoles() != null && !menuItem.getRoles().isEmpty() ) {
				roles = new StringBuffer();
				for ( Iterator i = menuItem.getRoles().iterator(); i.hasNext(); ) {
					Role role = (Role) i.next();
					roles.append(role.getName());
					if ( i.hasNext() ) {
						roles.append(",");
					}
				}
			}
			if ( roles != null ) {
				menuComponent.setRoles(roles.toString());
			} else {
				menuComponent.setRoles(null);
			}
			menuComponent.setTitle(WebappUtil.getFieldValue(menuItem.getTitle(), localeIdentifier, request, true));
			menuComponent.setToolTip(WebappUtil.getFieldValue(menuItem.getToolTip(), localeIdentifier, request, true));

			// recursive call of this method on child items

			List childItems = lookupItems(owners, menuItem.getId(), deleteInvisible);

			for ( Iterator i = childItems.iterator(); i.hasNext(); ) {
				MenuItem childItem = (MenuItem) i.next();
				menuComponent.addMenuComponent(convertToMenuComponent(owners, childItem, localeIdentifier, deleteInvisible));
			}

		} catch ( Exception e ) {
			if ( !(e instanceof MenuUtilException) ) {
				throw new MenuUtilException(e);
			} else {
				throw (MenuUtilException) e;
			}
		}

		return menuComponent;
	}

	/**
	 * Looks up owners. This list contains all parent layouts of layout that
     * have specified definition (and the given layout himself), and in the end
     * of list there is a page with specified URI.
	 * Each owner represents a layer. Menu items can be moved or modified
     * separately on each layer from the first parent layout to some page.
	 *
	 * @param layoutDefinition Definition of layout to lookup parents for
	 * @param pageUri          URI of page.
	 * @return List of owners
	 * @throws MenuUtilException if something wrong happens while looking up owners
	 */
	protected LinkedList lookupOwners(String layoutDefinition, String pageUri) throws MenuUtilException {
		LinkedList owners = new LinkedList();
		try {
			if ( pageUri != null ) {
				// page must be in the end of list
				PageManager pageManager = (PageManager) applicationContext.getBean(Constants.PAGE_MANAGER_BEAN);
				Page page = pageManager.findPageByUri(pageUri);
				if ( page != null ) {
					owners.add(page);
				}
			}

			// get all parent layouts for specified definition
			LayoutManager layoutManager = (LayoutManager) applicationContext.getBean(Constants.LAYOUT_MANAGER_BEAN);
			do {
				Layout layout = layoutManager.findLayoutByDefinition(layoutDefinition);
				if ( layout != null ) {
					owners.addFirst(layout);
				}
				layoutDefinition = ((HeritableComponentDefinition) TilesUtil.getDefinition(layoutDefinition, request, servletContext)).getExtends();
			} while ( layoutDefinition != null );
		} catch ( Exception e ) {
			throw new MenuUtilException(e);
		}

		// add null as first element.
		// null is the layer which hardcoded items belong to
		owners.addFirst(null);

		return owners;
	}

	/**
	 * Performs recursive copy of menu component
	 *
	 * @param menuComponent Menu component to get copy of
	 * @return Copy of menu component
	 */
	public MenuComponent recursiveCopy(MenuComponent menuComponent) {
		MenuComponent menu = new MenuComponent();
		try {
			MenuComponent parentMenu = menuComponent.getParent();
			menuComponent.setParent(null);
			WebappUtil.copyProperties(menu, menuComponent, request);
			menuComponent.setParent(parentMenu);
		} catch ( Exception e ) {
			if ( log.isErrorEnabled() ) {
				log.error("Failed to perform recursive copy of menu component.");
				StringWriter sw = new StringWriter();
				e.printStackTrace(new PrintWriter(sw));
				log.error(sw.toString());
			}
			return null;
		}
		MenuComponent[] childComponents = menuComponent.getMenuComponents();
		for ( int i = 0; i < childComponents.length; i++ ) {
			MenuComponent component = (MenuComponent) childComponents[i];
			MenuComponent copy = recursiveCopy(component);

			// check for null in order to correctly handle cases, when copying of properties failed
			if ( copy != null ) {
				menu.addMenuComponent(copy);
			}
		}

		return menu;
	}

	/**
	 * Searches for menu item with specified ID in list
	 *
	 * @param menuItems List of menu items to search in
	 * @param id        ID to search by
	 * @return Menu item or null if nothing found
	 */
	protected MenuItem findMenuItem(List menuItems, Long id) {
		MenuItem menuItem = null;
		for ( Iterator i = menuItems.iterator(); i.hasNext(); ) {
			menuItem = (MenuItem) i.next();
			if ( menuItem.getId().equals(id) ) {
				break;
			}
			menuItem = null;
		}
		return menuItem;
	}

	/**
	 * Searches for menu component with specified name in array of menu components.
	 * If nothing is found, returns null.
	 *
	 * @param menuComponents Array of menu components to search in
	 * @param name           Name to search by
	 * @return Menu component or null if none is found
	 */
	public static MenuComponent findMenuComponent(MenuComponent[] menuComponents, String name) {
		MenuComponent menuComponent = null;
		for ( int i = 0; i < menuComponents.length; i++ ) {
			if ( menuComponents[i].getName().equals(name) ) {
				menuComponent = menuComponents[i];
				break;
			}
		}
		return menuComponent;
	}

	/**
	 * Gets appropriate value for the menu location if location is <code>null</code>.
	 * If location is <code>null</code>, and the <code>page</code> attribute
     * exists, it's value will be set to the the value for page prepended with
	 * the context path of the application.
	 * If the <code>page</code> is <code>null</code>, and the <code>forward</code>
     * attribute exists, it's value will be looked up in struts-config.xml.
	 * <p/>
	 * <p>This method can be applied only to dynamic elements. It sets <code>location</code>
	 * attribute of MenuItem and does not process children.
	 * </p>
	 *
	 * @param menu Menu item to set location for
     * @param urlType indicates whether URL is context, domain or module
     * relative
     * @return menu item location
	 * @throws java.net.MalformedURLException thrown if URL is incorrect
	 */
	public String getLocation(MenuItem menu, int urlType)
	        throws MalformedURLException {

		String location = null;

		// if the location attribute is null,
		// then set it with a context relative page
		// attribute if it exists
		if ( menu.getLocation() == null ) {
			if ( menu.getAnchor() != null ) {
				location = new String();
				if ( urlType == WebappConstants.URL_TYPE_DOMAIN_RELATIVE ) {
					location = contextPath;
				}
				location += getAnchor(menu.getAnchor());
			} else if ( menu.getForward() != null ) {
				location = WebappUtil.getActionForwardURL(menu.getForward(), null, request, urlType);
			} else if ( menu.getAction() != null ) {
				location = WebappUtil.getActionMappingURL(menu.getAction(), null, request, urlType, false);
			}
		} else {
			location = menu.getLocation();
            if (!WebappUtil.isAbsoluteURL(location)
                    && !WebappUtil.isJavascriptURL(location)
                    && !menu.getExternalLocation().booleanValue() ) {
                if ( !location.startsWith("/") ) {
                    location = "/" + location;
                }
            }
		}

		return location;
	}

	/**
	 * Returns the value with anchor prepended with a "/"
	 * if it is not already.
	 *
	 * @param anchor The value for the anchor.
	 */
	protected String getAnchor(String anchor) {
		if ( anchor.startsWith("/") ) {
			return anchor;
		} else {
			anchor = "/" + anchor;
		}

		return anchor;
	}


    /**
     * Initializes menu repisitory
     */
    public MenuRepository initializeMenuRepository() {
        String menuConfig = servletContext.getInitParameter("menuConfigLocation");
        if ( menuConfig == null ) {
            menuConfig = "/WEB-INF/menu-config.xml";
        }

        MenuRepository menuRepository = new MenuRepository();
        menuRepository.setLoadParam(menuConfig);
        menuRepository.setServletContext(servletContext);
        try {
            menuRepository.load();

            if ( log.isDebugEnabled() ) {
                log.debug("struts-menu initialization successfull");
            }
        } catch ( LoadableResourceException lre ) {
            if ( log.isFatalEnabled() ) {
                log.fatal("Failure initializing struts-menu: " + lre.getMessage());
            }
        }

        // get top-level menus from repository
        List menuComponents = menuRepository.getTopMenus();

        // create or update redifinitions for menu components
        final ContentLocale defaultLocale = LocaleUtil.getInstance(servletContext).getDefaultLocale();
        final RoleManager roleManager = (RoleManager) applicationContext.getBean(Constants.ROLE_MANAGER_BEAN);

        // the following converter implementation is used to copy 'title', 'toolTip' and 'roles' properties
        Converter structuredPropertiesConverter = new Converter() {
            public Object convert(Class type, Object value) {

                if ( type == Map.class ) {
                    // converting title and tooltip
                    Map result = new HashMap();
                    result.put(defaultLocale.getIdentifier(), value);
                    return result;
                }
                if ( type == List.class ) {
                    // converting roles
                    List result = new ArrayList();
                    if ( value != null ) {
                        List roleNames = ConvertUtil.convertStringToList((String) value, ",", true);
                        for ( Iterator j = roleNames.iterator(); j.hasNext(); ) {
                            String roleName = (String) j.next();
                            Role role = roleManager.retrieveRole(roleName);
                            if ( role == null ) {
                                if ( log.isWarnEnabled() ) {
                                    log.warn("Role with name '" + roleName + " not found.");
                                }
                            } else {
                                result.add(role);
                            }
                        }
                    }
                    return result;
                } else {

                    // we don't know how to convert into unknown class
                    return null;
                }
            }
        };

        // register converter
        ConvertUtils.register(structuredPropertiesConverter, Map.class);
        ConvertUtils.register(structuredPropertiesConverter, List.class);

        processRedifinitions(menuComponents, null);

        // deregister converter
        ConvertUtils.deregister(Map.class);
        ConvertUtils.deregister(List.class);

        if ( log.isInfoEnabled() ) {
            log.info("Menu items from file " + menuConfig + " synchronized with database");
        }

        return menuRepository;
    }

    /**
     * Creates references to internal pages and resources, specified in 'action', 'location', 'image' and 'altImage' attributes in
     * menu config file. If links are external, empty list will be set.
     *
     * @param menuItem      Menu item to set references
     * @param menuComponent Menu component to get values of attributes from
     */
    protected void setLinkedObjects(MenuItem menuItem, MenuComponent menuComponent) {
        // create refs
        List linkedPages = new ArrayList();
        List linkedResources = new ArrayList();
        String image = menuComponent.getImage();
        BaseObject linkedObject = WebappUtil.lookupObject(image, servletContext, contextPath);
        if ( linkedObject != null && linkedObject instanceof ContentImage ) {
            linkedResources.add(linkedObject);
        }

        String altImage = menuComponent.getAltImage();
        if ( !GenericValidator.isBlankOrNull(altImage) && !altImage.equals(image) ) {
            linkedObject = WebappUtil.lookupObject(menuComponent.getAltImage(), servletContext, contextPath);
            if ( linkedObject != null && linkedObject instanceof ContentImage ) {
                linkedResources.add(linkedObject);
            }
        }

        // only 'location' and 'action' attributes are checked (I have no idea how to check 'forward' attribute from the listener)
        String uri = menuComponent.getLocation();

        if ( uri == null || uri.length() == 0 ) {
            uri = menuComponent.getAction();
        }

        if ( uri != null && uri.length() > 0 ) {
            linkedObject = WebappUtil.lookupObject(uri, servletContext, contextPath);
            if ( linkedObject != null ) {
                if ( linkedObject instanceof ContentResource ) {
                    linkedResources.add(linkedObject);
                } else if ( linkedObject instanceof Page ) {
                    linkedPages.add(linkedObject);
                }
            }
        }

        menuItem.setLinkedResources(linkedResources);
        menuItem.setLinkedPages(linkedPages);
    }

    /**
     * Creates redifinitions of hardcoded menu components in database
     *
     * @param menuComponents List of components
     * @param parentId       ID of parent menu item
     */
    protected void processRedifinitions(List menuComponents, final Long parentId) {
        MenuManager menuManager = (MenuManager) applicationContext.getBean(Constants.MENU_MANAGER_BEAN);

        // delete redifinitions which do not exist in repository
        List menuItems = menuManager.findMenuItemsByParentAndOwner(parentId, null);
        if ( menuItems != null ) {
            for ( Iterator i = menuItems.iterator(); i.hasNext(); ) {
                MenuItem menuItem = (MenuItem) i.next();
                MenuComponent menuComponent = MenuUtil.findMenuComponent((MenuComponent[]) menuComponents.toArray(new MenuComponent[0]), menuItem.getIdentifier());
                if ( menuComponent == null ) {
                    // component does not exist in repository, so we must delete it
                    try {
                        menuManager.deleteMenuItem(menuItem.getId());
                    } catch ( BeanNotFoundException e ) {
                        // bean not found - normally will never occur
                        if ( log.isErrorEnabled() ) {
                            StringWriter sw = new StringWriter();
                            e.printStackTrace(new PrintWriter(sw));
                            log.error(sw.toString());
                        }
                    }
                }
            }
        }

        // create non-existent redefinitions and update existent
        int k = 0;
        for ( Iterator i = menuComponents.iterator(); i.hasNext(); k++ ) {
            MenuComponent menuComponent = (MenuComponent) i.next();
            MenuItem menuItem = menuManager.findMenuItemByIdentifierAndParentAndOwner(menuComponent.getName(), parentId, null);

            Long parentItemId = null;

            if ( menuItem != null ) {
                // menu item already exists: update it
                try {
                    WebappUtil.copyProperties(menuItem, menuComponent);
                } catch ( Exception e ) {
                    if ( log.isFatalEnabled() ) {
                        StringWriter sw = new StringWriter();
                        e.printStackTrace(new PrintWriter(sw));
                        log.fatal("Failed to copy properties. Creation of redifinition skipped.");
                        log.fatal(sw.toString());
                    }
                    continue;
                }

                menuItem.setPosition(new Integer(k));
                parentItemId = menuItem.getId();
                try {
                    menuManager.updateMenuItem(menuItem, parentId, null);

                    // create references
                    setLinkedObjects(menuItem, menuComponent);
                } catch ( Exception e ) {
                    // normally this code should never be reached during normal execution of application
                    if ( log.isFatalEnabled() ) {
                        StringWriter sw = new StringWriter();
                        e.printStackTrace(new PrintWriter(sw));
                        log.fatal("Failed to update redifinition for component: " + menuComponent.getName() + ". Skipped.");
                        log.fatal(sw.toString());
                    }
                    continue;
                }
            } else {
                // create redifinition
                menuItem = new MenuItem();

                try {
                    WebappUtil.copyProperties(menuItem, menuComponent);
                } catch ( Exception e ) {
                    if ( log.isFatalEnabled() ) {
                        StringWriter sw = new StringWriter();
                        e.printStackTrace(new PrintWriter(sw));
                        log.fatal("Failed to copy properties. Creation of redifinition skipped.");
                        log.fatal(sw.toString());
                    }
                    continue;
                }

                menuItem.setPosition(new Integer(k));
                menuItem.setIdentifier(menuComponent.getName());
                menuItem.setVisibility(new Byte(MenuItem.VISIBILITY_VISIBLE));

                // create references
                setLinkedObjects(menuItem, menuComponent);

                try {
                    parentItemId = menuManager.createMenuItem(menuItem, parentId, null);
                } catch ( Exception e ) {
                    // normally this code should never be reached during normal execution of application
                    if ( log.isFatalEnabled() ) {
                        StringWriter sw = new StringWriter();
                        e.printStackTrace(new PrintWriter(sw));
                        log.fatal("Failed to create redifinition for component: " + menuComponent.getName() + ". Skipped.");
                        log.fatal(sw.toString());
                    }
                    continue;
                }
            }

            // recursive call of this method to process childs
            processRedifinitions(menuComponent.getComponents(), parentItemId);
        }

    }


}
